/**
 * JShark aka "jsk" v0.3 [build39]
 *
 * https://code.google.com/jshark
 *
 * Copyright (c) 2011 Intelygenz <www.intelygenz.com>
 * All rights reserved.
 * 
 * BSD License
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Creation date: 2011-08-18
 * 
 * [version] date (authors)
 * 		upd|fix|new|dep - description
 *
 * [0.3] 2011-09-20 (jonas.dacruz@intelygenz.com) 
 * 		 new - tested all methods
 * 		 new - refactor to Object-oriented Programming
 * 
 * [0.2] 2011-09-20 (jonas.dacruz@intelygenz.com) 
 * 		 new - create packages and refactor
 * [0.1] 2011-08-19 (jonas.dacruz@intelygenz.com, angel.sanchez@intelygenz.com, felix.sanchez@intelygenz.com) 
 * 		 new - designing and testing
 * 
 */
 
var JShark = jsk = new function (){
	var _Version = 0.3;
	var _Build	 = 39;
	var _Name	 = "JShark [jsk] v"+_Version+"b"+_Build;
	
	this.Name = function (){
		return _Name;
	}
}


/*****************************************
 * PACKAGES
 * 
 * 
 */
jsk.out   = {};
jsk.net   = {};
jsk.ui    = {};
jsk.dom   = {};
jsk.util  = {};
jsk.event = {};


/*****************************************
 * STATICS
 * 
 * 
 */

//////////////////////////// NET ///////////////////

(function() {	
	
	/// private
	
	var _BaseUri = "";

	/// public
	
	jsk.net.BaseUri = function (p_baseUri) {
		if ( p_baseUri ) {
			_BaseUri = p_baseUri;
		}
		return _BaseUri;
	}

})();

//////////////////////////// OUT ///////////////////

(function() {
	
	/// private
	
	var _LogLevel = 2;	// -1: no logs, 0: only errors, 1: errors & warnings, 2: all
	function _Error(p_a,p_b,p_c,p_d,p_e,p_f,p_g) {
		if (console.error  && _LogLevel>-1 ){
			console.error(jsk.Name(), p_a,p_b,p_c,p_d,p_e,p_f,p_g);
		}
	}
	function _Warning(p_a,p_b,p_c,p_d,p_e,p_f,p_g) {
		if (console.warn && _LogLevel>0 ){
			console.warn(jsk.Name(), p_a,p_b,p_c,p_d,p_e,p_f,p_g);
		}
	}
	function _Log(p_a,p_b,p_c,p_d,p_e,p_f,p_g) {
		if ( console.log && _LogLevel>1 ){
			console.log(jsk.Name(), p_a,p_b,p_c,p_d,p_e,p_f,p_g);
		}
	}
	
	/// public
	
	jsk.out.Error = function (p_a,p_b,p_c,p_d,p_e,p_f,p_g) { 
		_Error(p_a,p_b,p_c,p_d,p_e,p_f,p_g); 
	}
	
	jsk.out.Warning = function (p_a,p_b,p_c,p_d,p_e,p_f,p_g) { 
		_Warning(p_a,p_b,p_c,p_d,p_e,p_f,p_g); 
	}
	
	jsk.out.Log = function (p_a,p_b,p_c,p_d,p_e,p_f,p_g) { 
		_Log(p_a,p_b,p_c,p_d,p_e,p_f,p_g); 
	}

	jsk.out.SetLogLevel = function (p_logLevel) {
		 _LogLevel = p_logLevel;
	}

})();

//////////////////////////// DOM ///////////////////

(function() {

	/// private
	
	function _CheckId(p_dom, p_id){
		return p_dom.getAttribute("id") === p_id; 
	} 

	function _Find(p_dom, p_id){
		var node, child = null;
		for( var f=0, F=p_dom.childNodes.length; f<F && child==null; f++ ) {
			node = p_dom.childNodes[f];
			if( node.nodeType == 1 ) {
				if ( _CheckId(node, p_id) ) {
					child = node;
				} 
				else if(node.childNodes.length>0) {
					child = _Find(node, p_id);		// recursive find
				}
			}
		}
		return child;
	} 
	
	/// public
	
	jsk.dom.Find = function (p_dom, p_id) {
		return _Find(p_dom, p_id);	
	}

})();

////////////////////////////// UI ///////////////////

(function() {

	/// private 
	
	var _Loaded  = {};
	var _Loading = {};
	var _Failed  = {};

	/// public
	
	jsk.ui.Register = function (p_uiUri){
		_Loaded[p_uiUri] = 1;
	}

	function _Include( p_uiUri ) {
		if ( !_Loading[p_uiUri] && !_Loaded[p_uiUri] ) {
			_Loading[p_uiUri] = 1;
			var httpReq = new jsk.net.HttpRequest();
			httpReq.Sync (
					jsk.net.BaseUri() + p_uiUri
				, { }
				, { "dataType" : "html", "cache" : true }
				, function (p_httpResp){
					if ( p_httpResp.ErrorInfo() ){
						jsk.out.Error("jsk.ui.Include", p_httpResp.RequestInfo(), p_httpResp.ErrorInfo(), p_httpResp.Text() );
						_Failed[p_uiUri] = 1;
					}
					else {
						try {
							var head = document.getElementsByTagName("head")[0];
							var child;
							for ( var f=0, F=p_httpResp.Html().childNodes.length; f<F; f++ ){
								child = p_httpResp.Html().childNodes[f];
								if( child && child.nodeType == 1 ) {
									if( child.tagName.toUpperCase()=="SCRIPT" && child.getAttribute("type")=="text/javascript" ) {
										if ( child.getAttribute("src") ) {
											var script = document.createElement("script");
											script.setAttribute("type", child.getAttribute("type"));
											script.setAttribute("src", child.getAttribute("src"));
											head.appendChild( script );
										}
										else {
											if ( child.innerText ) {
												eval(child.innerText);
											}
											else if ( child.textContent ){
												eval(child.textContent);	
											}
										}
									}
									else {
										head.appendChild( child );
									}
								}
							}
							_Loaded[p_uiUri] = 1;
							
						}
						catch(p_err){
							jsk.out.Error("jsk.ui.Include", "imposible to append elements in <head>", p_err, document.getElementsByTagName("head") );
							_Failed[p_uiUri] = 1;
						}
					}
					_Loading[p_uiUri] = 0;
				}
			);
		}
	}

	jsk.ui.Include = function (){
		for ( var f=0, F=arguments.length; f<F; f++ ) {
			_Include(arguments[f]);
		}
	}

})();

//////////////////////////// UTIL ///////////////////

(function() {

	/// private
	
	
	/// public
	
	jsk.util.UniqueId = function (){
		return "jshark_"+ Math.floor(Math.random() * 1000000) +""+ (new Date().getTime());
	}
	
	// jsk.Parser.DomAttrsToObj(p_dom, p_hashObject)
	// Get attributes from DOM object and returns an hash with labels and values
	// [p_attr] : if p_attr exists it use to filter de response 
	jsk.util.DomAttributesToHashObject = function (p_dom, p_hashObject){
		var hashObj = {};
		if ( p_hashObject ){
			hashObj = p_hashObject;
		}
		var attrs = p_dom.attributes;
		var label;
		for( var f=0, F=attrs.length; f<F; f++ ) {
			label = attrs[f].name;
			if ( label.indexOf("_")==0 ){
				label = label.replace("_", "");
			}
			hashObj[label] = attrs[f].value;
		}
		return hashObj;
	}

	jsk.util.HashObjectToDomAttributes = function (p_hashObject, p_dom, p_filter){
		if ( p_filter ){
			for ( var f=0, F=p_filter.length; f<F; f++ ){
				p_dom.setAttribute(p_filter[f], p_hashObject[p_filter[f]]);
			}
		}
		else {
			for ( var label in p_hashObject){
				p_dom.setAttribute(label, p_hashObject[label]);
			}
		}
		return p_dom;
	}

	jsk.util.HashObjectToUriEncoded = function (p_hashObject){
		var uriEncoded = [];
		var encVal = "";
		for ( var label in p_hashObject ){
			encVal = escape( p_hashObject[label] );
			encVal = encVal.replace(/\+/g, encodeURIComponent("+"));
			encVal = encVal.replace(/u20AC/g, encodeURIComponent("80"));
			uriEncoded[uriEncoded.length] = label+"="+encVal+"&";
		}
		return uriEncoded.join("");
	}

})();


/*****************************************
 * CLASSES
 * 
 * 
 */

jsk.event.EventDispatcher = function (p_eventsRegistry){
	var _IsAwake  = false;
	var _Events   = [];
	var _Registry = {};

	// constructor
	function _EventDispatcher(){
		_Register(p_eventsRegistry);
	}

	// private
	function _Register(p_registry){
		for ( var reg in p_registry ){
			_Registry[reg] = p_registry[reg];
			_Events[p_registry[reg]] = [];
		}
	}

	// public 
	this.Registry = function (){
		return _Registry;
	}

	this.AddListener = function (p_listener, f_function){
		if ( !_Events[p_listener] ) {
			jsk.out.Error("Event listener ["+p_listener+"] is not supported by this object", this);
		}
		else {
			_Events[p_listener][ _Events[p_listener].length ] = f_function;
		}
	}
	
	this.Dispatch = function (p_listener, p_params){
		if ( _IsAwake ){
			for ( var f, F= _Events[p_listener]; f<F; f++ ){
				_Events[p_listener][f](p_params);
			}
		}
	}

	this.RemoveListener = function (p_listener, f_function){
		var indexToRemove = _Events[p_listener].indexOf();
		if ( indexToRemove!=-1 ){
			_Events[p_listener].splice(indexToRemove,1);
		}
		else {
			jsk.out.Warning("Event listener ["+p_listener+"] is not finded to remove it");
		}
	}

	this.RemoveAll = function (){
		for ( var listener in _Events ){
			for(var f=0, F=listener.length; f<F; f++){
				listener[f] = null;
			}
			listener = null;
		}
		_Events = [];
	}
	
	this.Awake = function (){
		_IsAwake = true;
	}

	this.Sleep = function (){
		_IsAwake = false;
	}

	// init
	_EventDispatcher();

}

jsk.net.HttpResponse = function (){
	var _HttpReq;
	var _ErrorInfo;
	var _RequestInfo;
	
	var _JSon;
	var _Xml;
	var _Html;
	var _DataTableList;
	var _DataTable;
	var _Text;

	function _ParseJSon(){
		try {
			eval("_JSon = "+_HttpReq.Xhr().responseText);
		}
		catch(p_err){
			_ErrorInfo = 
				{ "status"  : "200"
				, "message" : "malformed json"
				}
			;
		}
	}

	function _ParseDataTableList(){
		_ParseJSon();
		if ( typeof jsdtl == "undefined" ){
			_ErrorInfo = 
				{ "status"  : "200"
				, "message" : "missing library DataTableList"
				}
			;
		}
		else {
			_DataTableList = jsdtl.DataTableList( _JSon );
			if ( _HttpReq.DataTableName() && _HttpReq.DataTableName() != "*" ){
				_DataTable = _DataTableList.DataTable(_HttpReq.DataTableName());
			}
		}
	}

	function _Process(p_httpRequest){
		_HttpReq     = p_httpRequest;
		_RequestInfo = _HttpReq.Info(); 
		_ErrorInfo 	 = false;

		if ( _HttpReq.Xhr().readyState == 4 && _HttpReq.Xhr().status == 200 ) {
			_Text = _HttpReq.Xhr().responseText;
			switch ( _HttpReq.DataType() ){
				case "json" :
					_ParseJSon();
					break;
				case "jsdtl":
					_ParseDataTableList();
					break;
				case "xml":
					_Xml = _HttpReq.Xhr().responseXml;
					break;
				case "html":
					var body = document.createElement("body");
					body.innerHTML = _Text;
					_Html = body;
					break;
			}
		}
		else {
			_ErrorInfo = 
				{ "status"  : _HttpReq.Xhr().status
				, "message" : _HttpReq.Xhr().textStatus
				}
			;
		}
		return this;
	}

	// public methods
	this.ErrorInfo = function (){
		return _ErrorInfo;
	}
	this.RequestInfo = function (){
		return _RequestInfo;
	}
	this.Process = _Process;
	this.JSon = function () { return _JSon; }
	this.Xml = function () { return _Xml; }
	this.Html = function () { return _Html; }
	this.DataTableList = function () { return _DataTableList; }
	this.DataTable = function () { return _DataTable; }
	this.Text = function () { return _Text; }

}

jsk.net.HttpRequest = function (){
	var _HttpReq = this;
	var _RequestStatus = 
		{ "UNINITIALIZED": 0
		, "LOADING" 	 : 1
		, "LOADED" 		 : 2
		, "INTERACTIVE"	 : 3
		, "COMPLETE"	 : 4
		}
	;

	var _Uri;
	var _Params;
	var _OnFinish;
	var _OnChangeStatus;
	var _CurrentStatus;

	var _DefaultSettings =
		{ "method" 		: "GET"
		, "async"  		: true
		, "dataType"   	: "json"
		, "cache"  		: false
		, "dataTable" 	: ""
		}
	;
	var _CurrentSettings;

	var _XmlHttpReq;

	function _HttpResponse() {
		_XmlHttpReq = _GetXmlHttpReq();
	}
	
	function _GetXmlHttpReq(){
		var xhr;
	    if ( window.XMLHttpRequest ) {
	    	xhr = new XMLHttpRequest();		// standard
	    }
	    else {
	    	xhr = new ActiveXObject("Msxml2.XMLHTTP");	// compatibility with ie8
	    }
	    return xhr;
	}

	function _OnReadyStateChange(){
		var httpResp = (new jsk.net.HttpResponse()).Process(_HttpReq);
		if (_XmlHttpReq.readyState == 4 /* complete */) {
			if ( _OnFinish ) {
				_OnFinish(httpResp);
			}
		}
		else if ( _OnChangeStatus ){
			_OnChangeStatus(httpResp);
		}
	}

	function _Call (p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus){
		_Uri 	= p_uri;
		_Params = p_params;
		var paramsEncoded = jsk.util.HashObjectToUriEncoded( _Params );

		_OnFinish = f_onFinish;
		_OnChangeStatus = f_onChangeStatus;

		_CurrentSettings = _DefaultSettings;
		for ( var label in p_settings ){
			_CurrentSettings[label] = p_settings[label];
		}
		_XmlHttpReq.abort(); 
		if ( _CurrentSettings["method"].toUpperCase()=="GET" ) {
			_Uri+="?"+paramsEncoded;
		}
		_XmlHttpReq.open(_CurrentSettings["method"], _Uri, _CurrentSettings["async"]);
		_XmlHttpReq.onreadystatechange = _OnReadyStateChange;
		_XmlHttpReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
		if ( !_CurrentSettings["cache"] ) {
			_XmlHttpReq.setRequestHeader("Cache-Control", "no-store, no-cache, must-revalidate");
		}
		_XmlHttpReq.send( paramsEncoded );
	}

	function _Info() {
		var info = { 
			  "uri"      : _Uri
			, "params"   : _Params
			, "settings" : _CurrentSettings
			, "headers"  : _XmlHttpReq.getAllResponseHeaders()
//			, "xhr"		 : _XmlHttpReq
			}
		;
		return info;
	}

	// public methods
	this.Xhr = function (){
		return _XmlHttpReq;
	}
	this.Info = _Info;
	this.DataType = function (){
		return _CurrentSettings["dataType"];
	}
	this.DataTable = function (){
		return _CurrentSettings["dataTable"];
	}
	this.Async = function (p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus){
		if ( !p_settings ) {
			p_settings = {};
		}
		p_settings["async"] = true;
		_Call(p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus);
	}
	this.AsyncPost = function (p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus){
		if ( !p_settings ) {
			p_settings = {};
		}
		p_settings["method"] = "POST";
		this.Async(p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus);
	}
	this.Sync = function (p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus){
		if ( !p_settings ) {
			p_settings = {};
		}
		p_settings["async"] = false;
		_Call(p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus);
	}
	this.SyncPost = function (p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus){
		if ( !p_settings ) {
			p_settings = {};
		}
		p_settings["method"] = "POST";
		this.Sync(p_uri, p_params, p_settings, f_onFinish, f_onChangeStatus);
	}

	this.Cancel = function (){
		_XmlHttpReq.abort();
	}

	// init
	_HttpResponse();
}

jsk.dom.Template = function (p_tmplId, p_documentContext){
	var _Document = p_documentContext || document;

	var _Elements = {};
	var _Dom;

	function _Get(p_id){
		var ele = _Document.getElementById(_Elements[p_id]);
		if ( !ele ) {
			ele = jsk.dom.Find(_Dom, _Elements[p_id]);
		}
		if ( ele==null ){
			jsk.out.Error("Dom element not found", _Elements[p_id], p_id);
		}
		return ele;
	}

	function _Parse(p_tmplId){
		_Dom = _Document.createElement("span");
		if ( _Document.getElementById(p_tmplId) ) {
			var text = _Document.getElementById(p_tmplId).innerHTML;
			var id;
			var matches = text.match(/_id='[a-z0-9_]*'/gm);
			if ( matches!=null ) {
				for ( var f=0,F=matches.length; f<F; f++ ){
					id = matches[f];
					text = text.replace(id, id+" id='@@UNIQUE_ID@@' ");
					id = matches[f].replace("_id='", "");
					id = id.replace("'", "");
					_Elements[id] = jsk.util.UniqueId();
					text = text.replace("@@UNIQUE_ID@@", _Elements[id]);
				}
			}
			_Dom.innerHTML = text;
		}
		else {
			jsk.out.Error("jsk.dom.Template", "Template ["+p_tmplId+"] has not found");
		}
		return _Dom;
	}

	this.Get = _Get;
	this.Dom = function (){
		return _Dom;
	}

	_Parse(p_tmplId);
}

